---------Escape from Rungistan---------
A 4am crack                  2017-08-26
---------------------------------------

Name: Escape from Rungistan
Genre: adventure
Year: 1982
Credits: Bob Blauschild
Publisher: Sirius Software
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: DOS 3.3

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  read error on first pass

Locksmith Fast Disk Backup
  unable to read track $03-$22

EDD 4 bit copy (no sync, no count)
  no errors, but copy loads DOS, swings
  to high track, then honks and reboots
  (seriously, it's not a standard beep,
  it's a custom noise that sounds like
  a mildly annoyed goose)

Copy ][+ nibble editor
  T00-T02 standard
  T03-T21 modified address prologue
    $D5 $AA $F7
  T22 is weird, no structure, $F6 and
    $DE nibbles and not much else

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 22  START: 1800  LENGTH: 3DFF

29B8: F6 F6 F6 F6 F6 F6 F6 F6   VIEW
29C0: F6 F6 F6 F6 F6 F6 F6 F6
29C8: F6 F6 F6 F6 F6 F6 F6 F6
29D0: F6 F6 F6 F6 F6 F6 F6 F6
29D8: F6 F6 F6 F6 F6 F6 F6 AA
29E0: D5 D5 BD BD F6 AA D5 D5
29E8: F7 EF DE DE DE DE DE DE
29F0: DE DE DE DE DE DE DE DE
29F8: DE DE DE DE DE DE DE DE

                 --^--

Disk Fixer
  T00-T02 looks like standard DOS 3.3
  T01,S09 -> startup program is "START"
  ["O" -> "Input/Output Control"]
    set Address Epilogue to "D5 AA F7"
  Success! T03-T21 readable!
  T11 has a DOS 3.3 disk catalog
  "D" gets me a directory listing:

                 --v--

------------ DIRECTORY MODE -----------
$03:$0F  START
$14:$0F  LEG2
$15:$0F  NNN
$16:$0F  VOC1
$17:$0F  FIRSTF
$18:$0F  MID8
$19:$0F  SECHALF
$1A:$0F  LATE
$1B:$0F  PAIN5
$1C:$0F  COMPP2
$1D:$0F  HINTS
$1E:$0F  IND1
$1F:$0F  INDM
$20:$0F  IND2
$10:$0F  HT
$0F:$03  ST
$12:$0F  HOLD
$15:$0C  HS
$16:$05  HG
$1E:$0C  SG
$1F:$0D  HR
$20:$0C  SR
$0F:$01  FPBASIC
$1F:$01  APPLESOFT

                 --^--

Why didn't COPYA work?
  modified address prologue on T03+

Why didn't Locksmith FDB work?
  ditto

Why didn't my EDD copy work?
  probably a runtime check in late boot

Next steps:

  1. Convert disk to standard format
  2. Patch RWTS to read standard format
  3. Disable runtime protection check
  4. Declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
    You Can Hear A Lot By Listening


Listening to my non-working copy, it
sounds like it's loading DOS 3.3 from
tracks 2, 1, and 0, then it swings to a
high track (maybe that unreadable track
$22), then it reboots. It never gets as
far as displaying the standard "]"
prompt. The original disk does display
that prompt eventually; my working
theory is that there is a runtime
protection check embedded in the
bootloader itself. i.e. It never gets
as far as running the startup program.

A quick inspection in my trusty sector
editor confirms that the bootloader is
indistinguishable from an unprotected
DOS 3.3 disk, up to the point where it
jumps to $9D84 to initialize DOS and
run the startup program.

Thus, a trace program to capture all of
DOS in memory:

[S6,D1=original disk]
[S5,D1=my work disk]

]PR#5
...

]CALL -151

*9600<C600.C6FFM

; set up callback #1
96F8-   A9 4C       LDA   #$4C
96FA-   8D 4A 08    STA   $084A
96FD-   A9 0A       LDA   #$0A
96FF-   8D 4B 08    STA   $084B
9702-   A9 97       LDA   #$97
9704-   8D 4C 08    STA   $084C

; start the boot
9707-   4C 01 08    JMP   $0801

; callback #1 is here --
; set up callback #2 after DOS loads
970A-   A9 4C       LDA   #$4C
970C-   8D 47 B7    STA   $B747
970F-   A9 1C       LDA   #$1C
9711-   8D 48 B7    STA   $B748
9714-   A9 97       LDA   #$97
9716-   8D 49 B7    STA   $B749

; continue the boot
9719-   4C 00 B7    JMP   $B700

; callback #2 is here --
; copy DOS to lower memory (part of
; higher memory will be overwritten
; when I reboot to my work disk)
971C-   A2 23       LDX   #$23
971E-   A0 00       LDY   #$00
9720-   B9 00 9D    LDA   $9D00,Y
9723-   99 00 1D    STA   $1D00,Y
9726-   C8          INY
9727-   D0 F7       BNE   $9720
9729-   EE 22 97    INC   $9722
972C-   EE 25 97    INC   $9725
972F-   CA          DEX
9730-   D0 EE       BNE   $9720

; reboot to my work disk in slot 5
9732-   4C 00 C5    JMP   $C500

*BSAVE TRACE,A$9600,L$135
*9600G
...reboots slot 6...
...read read read...
...reboots slot 5...

]BSAVE OBJ.9D00-BFFF,A$1D00,L$2300
]CALL -151

*FE89G FE93G        ; disconnect DOS
*9D00<1D00.3FFFM    ; move captured DOS
                    ; into place
*9D84L

9D84-   A9 F7       LDA   #$F7
9D86-   8D 6A B9    STA   $B96A
9D89-   20 69 BA    JSR   $BA69
9D8C-   EA          NOP
9D8D-   EA          NOP
9D8E-   EA          NOP
9D8F-   EA          NOP
9D90-   EA          NOP
9D91-   EA          NOP
9D92-   EA          NOP
9D93-   EA          NOP
9D94-   AD 00 E0    LDA   $E000
9D97-   49 20       EOR   #$20
9D99-   D0 11       BNE   $9DAC

Oh.

                   ~

               Chapter 2
       Some Things Never Change
          And Some Things Do


$9D84 changes the third nibble of the
address prologue, which explains why I
couldn't copy those tracks. Then it
calls $BA69, which makes me suspicious.

*BA69L

; this is the code I expected to see at
; $9D84
BA69-   AD E9 B7    LDA   $B7E9
BA6C-   4A          LSR
BA6D-   4A          LSR
BA6E-   4A          LSR
BA6F-   4A          LSR
BA70-   8D 6A AA    STA   $AA6A
BA73-   AD EA B7    LDA   $B7EA
BA76-   8D 68 AA    STA   $AA68

; don't know what this is yet
BA79-   20 AF BE    JSR   $BEAF

; self-modify the line above to put an
; "RTS" where that "JSR" is
BA7C-   A9 60       LDA   #$60
BA7E-   8D 79 BA    STA   $BA79
BA81-   60          RTS

OK, the meat of this must be at $BEAF.
(Get it? Meat? $BEAF? I'mma keep my day
job.)

*BEAFL

; turn on drive motor manually
BEAF-   AE E9 B7    LDX   $B7E9
BEB2-   BD 89 C0    LDA   $C089,X

; seek to track $22 -- the unreadable
; track!
BEB5-   A9 44       LDA   #$44
BEB7-   20 A0 B9    JSR   $B9A0

; don't know what these are yet --
; maybe counters?
BEBA-   A9 04       LDA   #$04
BEBC-   8D 9F BF    STA   $BF9F
BEBF-   A9 00       LDA   #$00
BEC1-   8D A2 BF    STA   $BFA2
BEC4-   8D A0 BF    STA   $BFA0
BEC7-   A9 08       LDA   #$08
BEC9-   85 00       STA   $00
BECB-   A0 00       LDY   #$00
BECD-   8C A1 BF    STY   $BFA1

; look for custom nibble sequence
; "AA D5 D5 BD BD F6 AA D5 D5"
BED0-   BD 8C C0    LDA   $C08C,X
BED3-   10 FB       BPL   $BED0
BED5-   C9 AA       CMP   #$AA
BED7-   D0 F7       BNE   $BED0
BED9-   BD 8C C0    LDA   $C08C,X
BEDC-   10 FB       BPL   $BED9
BEDE-   C9 D5       CMP   #$D5
BEE0-   D0 F3       BNE   $BED5
BEE2-   BD 8C C0    LDA   $C08C,X
BEE5-   10 FB       BPL   $BEE2
BEE7-   C9 D5       CMP   #$D5
BEE9-   D0 EA       BNE   $BED5
BEEB-   BD 8C C0    LDA   $C08C,X
BEEE-   10 FB       BPL   $BEEB
BEF0-   C9 BD       CMP   #$BD
BEF2-   D0 E1       BNE   $BED5
BEF4-   BD 8C C0    LDA   $C08C,X
BEF7-   10 FB       BPL   $BEF4
BEF9-   C9 BD       CMP   #$BD
BEFB-   D0 D8       BNE   $BED5
BEFD-   BD 8C C0    LDA   $C08C,X
BF00-   10 FB       BPL   $BEFD
BF02-   C9 F6       CMP   #$F6
BF04-   D0 CF       BNE   $BED5
BF06-   BD 8C C0    LDA   $C08C,X
BF09-   10 FB       BPL   $BF06
BF0B-   C9 AA       CMP   #$AA
BF0D-   D0 C6       BNE   $BED5
BF0F-   BD 8C C0    LDA   $C08C,X
BF12-   10 FB       BPL   $BF0F
BF14-   C9 D5       CMP   #$D5
BF16-   D0 BD       BNE   $BED5
BF18-   BD 8C C0    LDA   $C08C,X
BF1B-   10 FB       BPL   $BF18
BF1D-   C9 D5       CMP   #$D5
BF1F-   D0 B4       BNE   $BED5

; skip 3 nibbles
BF21-   BD 8C C0    LDA   $C08C,X
BF24-   10 FB       BPL   $BF21
BF26-   BD 8C C0    LDA   $C08C,X
BF29-   10 FB       BPL   $BF26
BF2B-   BD 8C C0    LDA   $C08C,X
BF2E-   10 FB       BPL   $BF2B

; calculate an XOR checksum of the
; next $800 nibbles (Y=0, then
; incremented until it wraps around,
; and zp$00 was set to 8 at $BEC7)
BF30-   BD 8C C0    LDA   $C08C,X
BF33-   10 FB       BPL   $BF30
BF35-   4D A1 BF    EOR   $BFA1
BF38-   8D A1 BF    STA   $BFA1
BF3B-   C8          INY
BF3C-   D0 F2       BNE   $BF30
BF3E-   C6 00       DEC   $00
BF40-   D0 EE       BNE   $BF30

; calculate an XOR checksum of all
; the checksums (apparently we're going
; to do this all over again)
BF42-   AD A1 BF    LDA   $BFA1
BF45-   4D A2 BF    EOR   $BFA2
BF48-   8D A2 BF    STA   $BFA2

; $BF9F was set to 4 at $BEBC, which
; means we're calculating that $800
; nibble checksum 4 times (then
; calculating a master checksum of all
; those checksums)
BF4B-   CE 9F BF    DEC   $BF9F
BF4E-   F0 03       BEQ   $BF53
BF50-   4C CB BE    JMP   $BECB

; if master checksum is non-zero, skip
; the next two lines
BF53-   AD A2 BF    LDA   $BFA2
BF56-   D0 05       BNE   $BF5D

; set a flag
BF58-   A9 01       LDA   #$01
BF5A-   8D A0 BF    STA   $BFA0

; execution continues here regardless,
; and now we're looking for $100 $F6
; nibbles in a row
BF5D-   A0 80       LDY   #$80
BF5F-   BD 8C C0    LDA   $C08C,X
BF62-   10 FB       BPL   $BF5F
BF64-   C9 F6       CMP   #$F6
BF66-   D0 F7       BNE   $BF5F
BF68-   BD 8C C0    LDA   $C08C,X
BF6B-   10 FB       BPL   $BF68
BF6D-   C9 F6       CMP   #$F6
BF6F-   D0 EC       BNE   $BF5D
BF71-   88          DEY
BF72-   D0 F4       BNE   $BF68

; check that flag we set earlier (or
; maybe skipped)
BF74-   AD A0 BF    LDA   $BFA0

; if that flag is non-zero, branch to
; failure path
BF77-   D0 09       BNE   $BF82

; otherwise fall through, turn off the
; drive motor, seek back to track 0,
; and exit gracefully
BF79-   BD 88 C0    LDA   $C088,X
BF7C-   A9 00       LDA   #$00
BF7E-   20 A0 B9    JSR   $B9A0
BF81-   60          RTS

; failure path is here -- honk like a
; goose and reboot
BF82-   A0 20       LDY   #$20
BF84-   A9 20       LDA   #$20
BF86-   20 A8 FC    JSR   $FCA8
BF89-   AD 30 C0    LDA   $C030
BF8C-   88          DEY
BF8D-   D0 F5       BNE   $BF84
BF8F-   AD E9 B7    LDA   $B7E9
BF92-   4A          LSR
BF93-   4A          LSR
BF94-   4A          LSR
BF95-   4A          LSR
BF96-   18          CLC
BF97-   69 C0       ADC   #$C0
BF99-   8D 9E BF    STA   $BF9E
BF9C-   4C 00 00    JMP   $0000

There's a lot going on here, but it
boils down to this:

  1. find a long nibble prologue (that
     only appears once on the track)
  2. checksum the following nibbles
  3. do steps 1 and 2 repeatedly and
     make sure the checksum changes

This is the key point: the data being
read from track $22 is non-repeatable.
It's different every time it's read.
How is that possible?

The prologue ("AA D5 D5 BD BD F6 AA D5
D5") looks important, but it's not.
What's important is what comes after
it, what's being checksummed over and
over: nothing. Because that is what is
on the original disk: a long sequence
of zero bits, a.k.a. nothing.

Floppy disks are an analog medium. What
we call a "0 bit" is, in physicality,
"the lack of a magnetic state change."
If the Disk II doesn't see a flux state
change in a certain period of time, it
calls that a "0". If it does see a
change, it calls that a "1". But the
drive can't tolerate a lack of state
changes forever -- only about as long
as it takes for two bits to go by.

Fun fact(*): this is why you need to
use nibbles as an intermediate on-disk
format in the first place. No valid
nibble contains more than two zero bits
consecutively, when written from most-
significant to least-significant bit.

(*) not guaranteed, actual fun may vary

What happens when a drive doesn't see a
state change after the equivalent of
two consecutive zero bits? The drive
thinks the disk is weak, and it starts
increasing the amplification to try to
compensate, looking for a valid signal.
But there is no signal. There is no
data. There is only a yawning abyss of
nothingness. Eventually, the drive gets
desperate and amplifies beyond reason
and starts returning random bits based
on ambient noise from the disk motor
and the magnetism of the Earth.

Seriously.

Returning random bits doesn't sound
useful for a storage medium, but it's
exactly what the developer wanted, and
it's exactly what this code is checking
for. It's finding and reading and
checksumming the same sequence of bits
from the disk, over and over, and
checking that they change.

Bit copiers will never duplicate the
long sequence of zero bits, because
that's not what they read. Whatever
randomness they get when they read the
original disk will essentially get
"frozen" onto the copy. The checksum of
those frozen bits will always be the
same, no matter how many times you read
them. Each time the disk is read, the
$800-nibble checksum will be the same;
the master checksum -- the XOR of 4 of
those individual checksums -- will
always be 0; the flag at $BFA0 will
always get set; and we'll end up at
$BF82, honking like a goose.

God, I hate physical objects.

                   ~

               Chapter 3
In Which We Attempt To Use The Original
    Disk As a Weapon Against Itself


The plan, as laid out at the beginning,
has always been

  1. Convert disk to standard format
  2. Patch RWTS to read standard format
  3. Disable runtime protection check
  4. Declare victory (*)

We have enough information to implement
step 1. For "conversion of a disk for
which we have an RWTS," we turn to
Advanced Demuffin.

[S6,D1=original disk]
[S6,D2=blank disk]
[S5,D1=my work disk]

]PR#5
...

]BLOAD OBJ.9D00-BFFF,A$1D00
]BRUN ADVANCED DEMUFFIN 1.5

[press "X" to exit to monitor]

*B800<3800.3FFFM ; move RWTS into place

*B96A:F7         ; make the same change
                 ; as the bootloader
                 ; makes (at $9D84)

*800G            ; re-launch Advanced
                 ; Demuffin

[press "C" to convert disk]

["Y" to change default values]

We only want to copy tracks $03-$21.
Tracks 0-2 are standard, and track $21
is entirely devoted to the protection,
which we will disable later.

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======================================


INPUT ALL VALUES IN HEX


SECTORS PER TRACK? (13/16) 16

START TRACK: $03        <-- change this
START SECTOR: $00
END TRACK: $21          <-- change this
END SECTOR: $0F

INCREMENT: 1

MAX # OF RETRIES: 0

COPY FROM DRIVE 1
TO DRIVE: 2
=======================================
16SC $03,$00-$21,$0F BY1.0 S6,D1->S6,D2

                 --^--

And here we go...

                 --v--

ADVANCED DEMUFFIN 1.5    (C) 1983, 2014
ORIGINAL BY THE STACK    UPDATES BY 4AM
=======PRESS ANY KEY TO CONTINUE=======
TRK:   ...............................
+.5:
    0123456789ABCDEF0123456789ABCDEF012
SC0:   ...............................
SC1:   ...............................
SC2:   ...............................
SC3:   ...............................
SC4:   ...............................
SC5:   ...............................
SC6:   ...............................
SC7:   ...............................
SC8:   ...............................
SC9:   ...............................
SCA:   ...............................
SCB:   ...............................
SCC:   ...............................
SCD:   ...............................
SCE:   ...............................
SCF:   ...............................
=======================================
16SC $03,$00-$21,$0F BY1.0 S6,D1->S6,D2

                 --^--

Since tracks 0-2 are unprotected, I
used Copy II Plus manual sector copy to
copy them. (Not shown; it's under "bit
copy" if you're looking for it. Start
on track 0, end on track 2.)

Now I have a disk that boots but can't
read itself (above track 2), because it
still thinks that track 3 and above use
address prologue $D5 $AA $F7. They
don't; that's what I normalized with
Advanced Demuffin.

On to step 2: patching the RWTS to
coerce the disk into reading itself,
now that all tracks are in a standard
format. I will change the "STA $B96A"
to "BIT", which does nothing harmful
(but still uses 3 bytes to do it).

T00,S0C,$86: 8D -> 2C

On to step 3: disable the protection
routine. Since there is some required
code at $BA69 (replicated from the code
at $9D84 that was overwritten), I will
put an "RTS" at $BA79 -- which is what
the original disk does anyway after the
protection check succeeds.

T00,S04,$79: 20 -> 60

]PR#6
...works...

On to step 4... ;-)

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1386
------------------EOF------------------
